/**
 * Copyright (C) 2011 Audit Logger Project. All rights reserved.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.auditlogger.config.util;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class ObjectFactory {
	
	private static final Log logger = LogFactory.getLog(ObjectFactory.class);
	
	@SuppressWarnings("unchecked")
	public static <T> T make(String className, String typeName) throws ConfigurationException {
		logger.debug("inputs - className: " + className + " typeName: " + typeName);
		
		Object obj = null;
		String errorMessage = null;
		try {
			if (null == className || "".equals(className) ) {
				throw new IllegalArgumentException("Classname cannot be null or empty.");
			}
			if (null == typeName || "".equals(typeName) ) {
				typeName = "[unknown]";	
			}
			Class<?> theClass = Class.forName(className);
            try {
                Method singleton = theClass.getMethod( "getInstance" );
                if ( !Modifier.isStatic( singleton.getModifiers() ) )
                {
                    throw new ConfigurationException( "Class [" + className + 
                    		"] contains a non-static getInstance method." );
                }
                obj = singleton.invoke( null );
            } catch (NoSuchMethodException e) {
                obj = theClass.newInstance();
            } catch (SecurityException e) {
                throw new ConfigurationException(e.getMessage(), e);
            }
			return (T)obj;		
		} catch( IllegalArgumentException ex ) {
			errorMessage = ex.toString() + " " + typeName + " type name cannot be null or empty.";
			throw new ConfigurationException(errorMessage, ex);
		}catch ( ClassNotFoundException ex ) {
			errorMessage = ex.toString() + " " + typeName + " class [" + className + "] must be in class path.";
			throw new ConfigurationException(errorMessage, ex);
		} catch( InstantiationException ex ) {
			errorMessage = ex.toString() + " " + typeName + " class [" + className + "] must be a concrete class.";
			throw new ConfigurationException(errorMessage, ex);
		} catch( IllegalAccessException ex ) {
			errorMessage = ex.toString() + " " + typeName + " class [" + className 
			     + "] must have a public, no-arg constructor.";
			throw new ConfigurationException(errorMessage, ex);
		} catch( ClassCastException ex ) {
			errorMessage = ex.toString() + " " + typeName 
			    + " class [" + className + "] must be a subtype of T in ObjectFactory<T>";
			throw new ConfigurationException(errorMessage, ex);
		} catch( Exception ex ) {
			errorMessage = ex.toString() + " " + typeName + " class [" + className 
			     + "] CTOR threw exception";
			throw new ConfigurationException(errorMessage, ex);
		}
	}	

}
